package Framework.Serialize
{
	import Framework.Util.*;
	import flash.utils.*;
	
	public class SerializeStream extends Object
	{
		private var _byteArray:ByteArray;
		private var _seqDataStack:SeqData = null;
		
		public function SerializeStream()
		{
			this.byteArray = new ByteArray();
		}
		
		public function readByte() : int
		{
			return this._byteArray.readByte();
		}
		
		public function readByteSeq() : Array
		{
			var data:Array = new Array();
			var size:int = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				data.push(this.readByte());
				i++;
			}
			
			return data;
		}
		
		public function writeByte(byte:int) : void
		{
			this._byteArray.writeByte(byte);
		}
		
		public function writeByteSeq(bytes:Array) : void
		{
			if (bytes == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(bytes.length);
			var i:int = 0;
			while (i < bytes.length)
			{
				
				this.writeByte(bytes[i]);
				i++;
			}
		}
		
		public function writeBool(bol:Boolean) : void
		{
			if (bol)
			{
				this._byteArray.writeByte(1);
			}
			else
			{
				this._byteArray.writeByte(0);
			}
		}
		
		public function writeBoolSeq(bols:Array) : void
		{
			if (bols == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(bols.length);
			var i:int = 0;
			while (i < bols.length)
			{
				
				this.writeBool(bols[i]);
				i++;
			}
		}
		
		public function readBool() : Boolean
		{
			var byte:* = this._byteArray.readByte();
			return !byte == 0;
		}
		
		public function readBoolSeq() : Array
		{
			var bols:Array = new Array();
			var size:int = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				bols.push(this.readBool());
				i++;
			}
			
			return bols;
		}
		
		public function readSize() : int
		{
			var byte:int = this._byteArray.readUnsignedByte();
			if (byte == 255)
			{
				return this._byteArray.readInt();
			}
			return byte;
		}
		
		public function writeSize(size:int) : void
		{
			if (size < 255)
			{
				this._byteArray.writeByte(size);
			}
			else
			{
				this._byteArray.writeByte(255);
				this._byteArray.writeInt(size);
			}
		}
		
		public function readInt() : int
		{
			return this._byteArray.readInt();
		}
		
		public function readIntSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readInt());
				i++;
			}
			return arr;
		}
		
		public function writeInt(data:int) : void
		{
			this._byteArray.writeInt(data);
		}
		
		public function writeIntSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeInt(arr[i]);
				i++;
			}
		}
		
		public function readShort() : int
		{
			return this._byteArray.readShort();
		}
		
		public function readShortSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readShort());
				i++;
			}
			return arr;
		}
		
		public function writeShort(data:int) : void
		{
			this._byteArray.writeShort(data);
		}
		
		public function writeShortSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeShort(arr[i]);
				i++;
			}
		}
		
		public function readLong() : Number
		{
			var i:* = this._byteArray.readInt();
			var j:* = this._byteArray.readInt();
			return j * (uint.MAX_VALUE + 1) + i;
		}
		
		public function readLongSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readLong());
				i++;
			}
			return arr;
		}
		
		public function writeLong(data:Number) : void
		{
			var multiple:* = data % (uint.MAX_VALUE + 1);
			var remainder:* = data / (uint.MAX_VALUE + 1);
			if (data >= 0)
			{
				this._byteArray.writeUnsignedInt(multiple);
				this._byteArray.writeInt(remainder);
			}
			else if (remainder == 0)
			{
				this._byteArray.writeUnsignedInt(data);
				this._byteArray.writeInt(-1);
			}
			else
			{
				this._byteArray.writeUnsignedInt(multiple);
				this._byteArray.writeInt(remainder);
			}
		}
		
		public function writeLongSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeLong(arr[i]);
				i++;
			}
		}
		
		public function readString() : String
		{
			var size:* = this.readSize();
			return this._byteArray.readUTFBytes(size);
		}
		
		public function readStringSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readString());
				i++;
			}
			return arr;
		}
		
		public function writeString(str:String) : void
		{
			if (str == null)
			{
				this.writeSize(0);
				
			}
			var byte:* = new ByteArray();
			byte.writeUTFBytes(str);
			this.writeSize(byte.length);
			this._byteArray.writeBytes(byte);
		}
		
		public function writeStringSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeString(arr[i]);
				i++;
			}
		}
		
		public function readFloat() : Number
		{
			return this._byteArray.readFloat();
		}
		
		public function readFloatSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readFloat());
				i++;
			}
			return arr;
		}
		
		public function writeFloat(f:Number) : void
		{
			this._byteArray.writeFloat(f);
		}
		
		public function writeFloatSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeFloat(arr[i]);
				i++;
			}
		}
		
		public function readDouble() : Number
		{
			return this._byteArray.readDouble();
		}
		
		public function readDoubleSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readDouble());
				i++;
			}
			return arr;
		}
		
		public function writeDouble(data:Number) : void
		{
			this._byteArray.writeDouble(data);
		}
		
		public function writeDoubleSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeDouble(arr[i]);
				i++;
			}
		}
		
		public function readDate() : Date
		{
			return new Date(this.readInt() * 1000);
		}
		
		public function readDateSeq() : Array
		{
			var arr:* = new Array();
			var size:* = this.readSize();
			var i:int = 0;
			while (i < size)
			{
				
				arr.push(this.readDate());
				i++;
			}
			return arr;
		}
		
		public function writeDate(date:Date) : void
		{
			this._byteArray.writeInt(date.time / 1000);
		}
		
		public function writeDateSeq(arr:Array) : void
		{
			if (arr == null)
			{
				this.writeSize(0);
				
			}
			this.writeSize(arr.length);
			var i:int = 0;
			while (i < arr.length)
			{
				
				this.writeDate(arr[i]);
				i++;
			}
		}
		
		public function writeByteArray(stream:SerializeStream) : void
		{
			this.byteArray.writeBytes(stream.byteArray);
		}
		
		public function writeException(exception:Exception) : void
		{
			this.writeString(exception.message);
			this.writeInt(exception.code);
		}
		
		public function readException(exception:Exception) : void
		{
			exception.message = this.readString();
			exception.code = this.readInt();
		}
		
		public function startSeq(i:int, j:int) : void
		{
			if (j == 0)
			{
				
			}
			var seq:* = new SeqData(i, j);
			seq.previous = this._seqDataStack;
			this._seqDataStack = seq;
			var leftbyte:* = this.getBytesLeft();
			if (this._seqDataStack.previous == null)
			{
				if (i * j > leftbyte)
				{
					throw new SerializeException();
				}
			}
			else
			{
				this.checkSeqLen(leftbyte);
			}
		}
		
		public function checkSeq() : void
		{
			this.checkSeqLen(this.getBytesLeft());
		}
		
		public function checkSeqLen(len:int) : void
		{
			var i:int = 0;
			var child:* = this._seqDataStack;
			do
			{
				
				i = i + (child.numElements - 1) * child.minSize;
				child = child.previous;
			}while (child != null)
			
			if (i > len)
			{
				throw new SerializeException();
			}
		}
		
		public function endElement() : void
		{
			this._seqDataStack.numElements = this._seqDataStack.numElements - 1;
		}
		
		public function endSeq(end:int) : void
		{
			if (end == 0)
			{
				
			}
			this._seqDataStack = this._seqDataStack.previous;
		}
		
		public function checkFixedSeq(i:int, j:int) : void
		{
			var left:int = this.getBytesLeft();
			if (this._seqDataStack == null)
			{
				if (i * j > left)
				{
					throw new SerializeException();
				}
			}
			else
			{
				this.checkSeqLen(left - i * j);
			}
		}
		
		public function getBytesLeft() : int
		{
			return this.byteArray.length - this.byteArray.position;
		}
		
		public function clear() : void
		{
			this.byteArray = new ByteArray();
		}
		
		public function set byteArray(byteArray:ByteArray) : void
		{
			this._byteArray = byteArray;
			this._byteArray.endian = Endian.LITTLE_ENDIAN;
		}
		
		public function get byteArray() : ByteArray
		{
			return this._byteArray;
		}
		
	}
}
